#if !defined(__HTTP_SERVER_H__)
#define __HTTP_SERVER_H__

#include "sdkconfig.h"
#include <stdint.h>
#include "esp_http_server.h"
#include <sys/socket.h>

#include "cJSON.h"

#define WEB_BUFFER_SIZE 4096

#define HTTPD_SERVER_ENABLE_AUTH 0
#define HTTPD_SERVER_ENABLE_WS 1
#define HTTPD_INDEX_FILE_NAME "index.html.gz"

#if HTTPD_SERVER_ENABLE_AUTH
typedef struct http_server_auth_key
{
    char ip[16];
    uint32_t key;
    uint64_t timestamp; /* us */
    uint8_t auth;
    struct http_server_auth_key *next;
} http_server_auth_key_s, *http_server_auth_key_t;

/* 40byte */
typedef struct http_server_auth
{
    struct http_server_auth *next;
    char name[21];   /* 用户名 */
    char passwd[17]; /* 用户密码 */
    char path[129];  /* 用户路径 */
    uint8_t type;    /* 类型 */
} http_server_auth_s, *http_server_auth_t;
#endif

#if HTTPD_SERVER_ENABLE_WS
typedef struct http_server_ws_clinet
{
    struct http_server_ws_clinet *next;
    uint8_t shell : 1;
#if HTTPD_SERVER_ENABLE_AUTH
    uint8_t auth : 1; /* 授权状态 */
    uint32_t key;     /* 授权key */
#endif
    int fd;              /* sock */
    uint64_t alive_time; /* 存活时间 */
} http_server_ws_clinet_s, *http_server_ws_clinet_t;
typedef struct http_server_ws_message
{
    struct http_server_ws_message *next;
    void *user_data;
    int fd;
    uint16_t len;
    uint16_t read;
    uint8_t data[];
} http_server_ws_message_s, *http_server_ws_message_t;
#endif

typedef struct http_server
{
    httpd_handle_t server;
    char path[512];
    char name[256];
#if HTTPD_SERVER_ENABLE_AUTH
    http_server_auth_t user;
    http_server_auth_key_t auth_ip;
#endif
#if HTTPD_SERVER_ENABLE_WS
    struct
    {
        uint8_t in_process : 1;     /* WS正在处理 */
        uint8_t process : 1;     /* WEB正在处理 */
        uint8_t watting_msg : 1; /* 等待消息 */
        uint16_t length;
        uint16_t msg_len;
        httpd_req_t *req;
        portMUX_TYPE lock;
        SemaphoreHandle_t send_lock;
        http_server_ws_clinet_t clients;
        http_server_ws_message_t msg_list;
        void *msg_data;
        uint32_t msg_count;
        int last_fd;
        uint32_t keep_alive_period_ms;
        FILE *file;              /* 更新文件 */
        uint32_t file_size;      /* 更新文件大小 */
        uint32_t file_recv_size; /* 已接收文件大小 */
        uint8_t data[WEB_BUFFER_SIZE];
#if HTTPD_SERVER_ENABLE_AUTH
        uint32_t auth_list[20];
        uint64_t auth_time[20];
#endif
    } ws;
#endif
    httpd_req_t *cur_req;
    uint8_t data[WEB_BUFFER_SIZE + 64];
} http_server_s, *http_server_t;
extern http_server_t http_server;

#define cJSON_GetInt(json, item) cJSON_GetObjectItem(json, item)->valueint
#define cJSON_GetDouble(json, item) cJSON_GetObjectItem(json, item)->valuedouble

#ifndef MIN
#define MIN(x, y) (x > y ? y : x)
#endif

#define req_send_err(str)                                                  \
    do                                                                     \
    {                                                                      \
        printf(#str "\n");                                                 \
        httpd_resp_sendstr_chunk(req, "{\"code\":-1,\"value\":" #str "}"); \
    } while (0)
#define req_send_ok(str)                               \
    do                                                 \
    {                                                  \
        httpd_resp_sendstr_chunk(req, "{\"code\":0}"); \
    } while (0)

#define send_blob_file(name, type)                                            \
    extern const unsigned char name##_start[] asm("_binary_" #name "_start"); \
    extern const unsigned char name##_end[] asm("_binary_" #name "_end");     \
    const size_t name##_size = (name##_end - name##_start);                   \
    httpd_resp_set_type(req, type);                                           \
    httpd_resp_send_chunk(req, (const char *)name##_start, name##_size);      \
    httpd_resp_sendstr_chunk(req, NULL);

const char *get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize);
/**
 *      check ip is auth
 *      yes return 0
 *      no return 1
 */
uint8_t http_server_check_ip_auth(httpd_req_t *req);
/**
 *      Get req ip
 */
char *http_server_get_req_ip(httpd_req_t *req);
/**
 *      将数据转换成hex字符串(静态字符串缓冲区, 不需要free)
 *      最多将16个byte数据转成字符串
 *      buffer  : 数据缓冲区
 *      len     : 缓冲区长度
 *      成功返回 静态 字符串指针
 *      失败返回 0
 */
char *get_hex_str(const uint8_t *buffer, uint8_t len);
/**
 *      获取json 字段的字符串数据
 *      成功返回 指针
 *      失败返回 0 并且发送错误数据到客户端
 */
char *json_get_string(httpd_req_t *req, cJSON *json, char *item);
/**
 *      检测cjson对象中是否存在 指定字段
 *      name    : 指定字段
 *      存在返回 1
 *      不存在返回 0 , 并且发送错误数据到客户端
 */
uint8_t json_object_has_item(httpd_req_t *req, cJSON *json, char *name);
/**
 *      将请求中携带的内容, 转换成 json 对象
 *      req     : http server 获取到的请求指针
 *      成功返回 json对象指针
 *      失败返回 0
 */
cJSON *http_server_data_parse_to_json(httpd_req_t *req, uint8_t is_obj);
#if HTTPD_SERVER_ENABLE_AUTH
/**
 *      查找 是否 存在某个授权用户
 *      name    : 授权用户名
 *      成功返回 授权对象指针
 *      失败返回 0
 */
http_server_auth_t http_server_find_user(char *name);
/**
 *      添加授权用户
 *      name    : 授权用户名
 *      passwd  : 授权用户的明文密钥
 *      成功返回 授权对象指针
 *      失败返回 0
 */
http_server_auth_t http_server_add_user(char *name, char *passwd);
#endif
/**
 *      md5校验
 *      name    : 用户名
 *      passwd  : 明文密钥
 *      key     : 临时授权密钥
 *      md5_str : 需要比较的md5字符串
 *      成功返回 1
 *      失败返回 0
 */
uint8_t md5_check(char *name, char *passwd, uint32_t key, char *md5_str);
/**
 *      md5校验码
 *      name    : 用户名
 *      passwd  : 明文密钥
 *      key     : 临时授权密钥
 *      返回    : md5校验码
 */
char *md5_get_user_passwd(char *name, char *passwd, uint32_t key);
/**
 *      检查当前会话是否已经授权
 *      成功返回 0
 *      失败返回 1 并且发送 code : -1, 提示此会话没有授权
 */
uint8_t http_server_auth_check(httpd_req_t *req);
#if HTTPD_SERVER_ENABLE_WS
uint16_t ws_msg_read_bytes(uint8_t *data, uint16_t len);
uint16_t ws_msg_reply(uint8_t *data, uint16_t len);
uint16_t ws_send_msg(uint8_t *data, uint16_t len);
void ws_reply_status(http_server_t server, httpd_ws_frame_t *frame, char *name, int32_t status);
void ws_reply_data(http_server_t server, httpd_ws_frame_t *frame, char *name, char *data);
#endif

#endif // __HTTP_SERVER_H__
